using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using AutoMapper;
using AiPlatformAdmin.Api.Data;
using AiPlatformAdmin.Api.Models;
using AiPlatformAdmin.Api.Services;
using AiPlatformAdmin.Api.DTOs;

namespace AiPlatformAdmin.Api.Controllers;

[ApiController]
[Route("api/knowledge-bases")]
[Authorize]
public class KnowledgeBasesController : ControllerBase
{
    private readonly AppDbContext _context;
    private readonly ITenantService _tenantService;
    private readonly IMapper _mapper;

    public KnowledgeBasesController(AppDbContext context, ITenantService tenantService, IMapper mapper)
    {
        _context = context;
        _tenantService = tenantService;
        _mapper = mapper;
    }

    private int GetCurrentUserId()
    {
        var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return int.TryParse(userIdClaim, out int userId) ? userId : 0;
    }

    private async Task<bool> CanUserAccessKnowledgeBase(int kbId, int userId)
    {
        var kb = await _context.KnowledgeBases.FindAsync(kbId);
        if (kb == null) return false;

        if (User.IsInRole("Administrator")) return true;
        if (kb.IsPublic) return true;

        if (await _context.KnowledgeBaseAdmins.AnyAsync(kba => kba.KnowledgeBaseId == kbId && kba.UserId == userId))
            return true;

        var userTeamIds = await _context.UserTeams
            .Where(ut => ut.UserId == userId)
            .Select(ut => ut.TeamId)
            .ToListAsync();

        return await _context.KnowledgeBaseTeamAccesses
            .AnyAsync(kbta => kbta.KnowledgeBaseId == kbId && userTeamIds.Contains(kbta.TeamId));
    }

    private async Task<bool> IsUserKnowledgeBaseAdmin(int kbId, int userId)
    {
        if (User.IsInRole("Administrator")) return true;
        return await _context.KnowledgeBaseAdmins.AnyAsync(kba => kba.KnowledgeBaseId == kbId && kba.UserId == userId);
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<KnowledgeBaseDto>>> GetKnowledgeBases()
    {
        var userId = GetCurrentUserId();
        List<KnowledgeBase> knowledgeBases;

        if (User.IsInRole("Administrator"))
        {
            knowledgeBases = await _context.KnowledgeBases
                .Include(kb => kb.KnowledgeBaseAdmins)
                .Include(kb => kb.KnowledgeBaseTeamAccesses)
                .Include(kb => kb.Documents)
                .ToListAsync();
        }
        else
        {
            var userTeamIds = await _context.UserTeams
                .Where(ut => ut.UserId == userId)
                .Select(ut => ut.TeamId)
                .ToListAsync();

            var adminKbIds = await _context.KnowledgeBaseAdmins
                .Where(kba => kba.UserId == userId)
                .Select(kba => kba.KnowledgeBaseId)
                .ToListAsync();

            var teamAccessKbIds = await _context.KnowledgeBaseTeamAccesses
                .Where(kbta => userTeamIds.Contains(kbta.TeamId))
                .Select(kbta => kbta.KnowledgeBaseId)
                .ToListAsync();

            var accessibleKbIds = adminKbIds.Union(teamAccessKbIds).ToList();

            knowledgeBases = await _context.KnowledgeBases
                .Include(kb => kb.KnowledgeBaseAdmins)
                .Include(kb => kb.KnowledgeBaseTeamAccesses)
                .Include(kb => kb.Documents)
                .Where(kb => kb.IsPublic || accessibleKbIds.Contains(kb.Id))
                .ToListAsync();
        }

        return Ok(_mapper.Map<IEnumerable<KnowledgeBaseDto>>(knowledgeBases));
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<KnowledgeBaseDto>> GetKnowledgeBase(int id)
    {
        var userId = GetCurrentUserId();
        if (!await CanUserAccessKnowledgeBase(id, userId))
        {
            return Forbid();
        }

        var knowledgeBase = await _context.KnowledgeBases
            .Include(kb => kb.KnowledgeBaseAdmins)
            .Include(kb => kb.KnowledgeBaseTeamAccesses)
            .Include(kb => kb.Documents)
            .FirstOrDefaultAsync(kb => kb.Id == id);

        if (knowledgeBase == null)
        {
            return NotFound();
        }

        return Ok(_mapper.Map<KnowledgeBaseDto>(knowledgeBase));
    }

    [HttpPost]
    [Authorize(Roles = "PlatformUser,Administrator")]
    public async Task<ActionResult<KnowledgeBaseDto>> CreateKnowledgeBase(CreateKnowledgeBaseDto dto)
    {
        if (!_tenantService.CurrentTenantId.HasValue)
        {
            return Forbid();
        }

        var userId = GetCurrentUserId();
        var knowledgeBase = _mapper.Map<KnowledgeBase>(dto);

        _context.KnowledgeBases.Add(knowledgeBase);
        await _context.SaveChangesAsync();

        // Make the creator an admin
        if (!User.IsInRole("Administrator"))
        {
            _context.KnowledgeBaseAdmins.Add(new KnowledgeBaseAdmin
            {
                KnowledgeBaseId = knowledgeBase.Id,
                UserId = userId
            });
        }

        // Add admin users
        if (dto.AdminUserIds != null && dto.AdminUserIds.Any())
        {
            foreach (var adminUserId in dto.AdminUserIds)
            {
                if (!await _context.KnowledgeBaseAdmins.AnyAsync(kba => kba.KnowledgeBaseId == knowledgeBase.Id && kba.UserId == adminUserId))
                {
                    _context.KnowledgeBaseAdmins.Add(new KnowledgeBaseAdmin { KnowledgeBaseId = knowledgeBase.Id, UserId = adminUserId });
                }
            }
        }

        // Add team accesses
        if (dto.TeamAccessIds != null && dto.TeamAccessIds.Any())
        {
            foreach (var teamId in dto.TeamAccessIds)
            {
                _context.KnowledgeBaseTeamAccesses.Add(new KnowledgeBaseTeamAccess { KnowledgeBaseId = knowledgeBase.Id, TeamId = teamId });
            }
        }

        await _context.SaveChangesAsync();

        // Reload with all relationships
        await _context.Entry(knowledgeBase).Collection(kb => kb.KnowledgeBaseAdmins).LoadAsync();
        await _context.Entry(knowledgeBase).Collection(kb => kb.KnowledgeBaseTeamAccesses).LoadAsync();
        await _context.Entry(knowledgeBase).Collection(kb => kb.Documents).LoadAsync();

        return CreatedAtAction(nameof(GetKnowledgeBase), new { id = knowledgeBase.Id }, _mapper.Map<KnowledgeBaseDto>(knowledgeBase));
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateKnowledgeBase(int id, UpdateKnowledgeBaseDto dto)
    {
        var userId = GetCurrentUserId();
        if (!await IsUserKnowledgeBaseAdmin(id, userId))
        {
            return Forbid();
        }

        var knowledgeBase = await _context.KnowledgeBases
            .Include(kb => kb.KnowledgeBaseAdmins)
            .Include(kb => kb.KnowledgeBaseTeamAccesses)
            .FirstOrDefaultAsync(kb => kb.Id == id);

        if (knowledgeBase == null)
        {
            return NotFound();
        }

        _mapper.Map(dto, knowledgeBase);

        // Update admins if provided
        if (dto.AdminUserIds != null)
        {
            var existingAdmins = knowledgeBase.KnowledgeBaseAdmins.Select(kba => kba.UserId).ToList();
            var newAdmins = dto.AdminUserIds.ToList();

            var toAdd = newAdmins.Except(existingAdmins);
            var toRemove = existingAdmins.Except(newAdmins);

            foreach (var adminUserId in toAdd)
            {
                _context.KnowledgeBaseAdmins.Add(new KnowledgeBaseAdmin { KnowledgeBaseId = id, UserId = adminUserId });
            }

            foreach (var adminUserId in toRemove)
            {
                var adminToRemove = knowledgeBase.KnowledgeBaseAdmins.FirstOrDefault(kba => kba.UserId == adminUserId);
                if (adminToRemove != null)
                {
                    _context.KnowledgeBaseAdmins.Remove(adminToRemove);
                }
            }
        }

        // Update team accesses if provided
        if (dto.TeamAccessIds != null)
        {
            var existingTeams = knowledgeBase.KnowledgeBaseTeamAccesses.Select(kbta => kbta.TeamId).ToList();
            var newTeams = dto.TeamAccessIds.ToList();

            var toAdd = newTeams.Except(existingTeams);
            var toRemove = existingTeams.Except(newTeams);

            foreach (var teamId in toAdd)
            {
                _context.KnowledgeBaseTeamAccesses.Add(new KnowledgeBaseTeamAccess { KnowledgeBaseId = id, TeamId = teamId });
            }

            foreach (var teamId in toRemove)
            {
                var teamAccessToRemove = knowledgeBase.KnowledgeBaseTeamAccesses.FirstOrDefault(kbta => kbta.TeamId == teamId);
                if (teamAccessToRemove != null)
                {
                    _context.KnowledgeBaseTeamAccesses.Remove(teamAccessToRemove);
                }
            }
        }

        await _context.SaveChangesAsync();
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteKnowledgeBase(int id)
    {
        var userId = GetCurrentUserId();
        if (!await IsUserKnowledgeBaseAdmin(id, userId))
        {
            return Forbid();
        }

        var knowledgeBase = await _context.KnowledgeBases.FindAsync(id);
        if (knowledgeBase == null)
        {
            return NotFound();
        }

        _context.KnowledgeBases.Remove(knowledgeBase);
        await _context.SaveChangesAsync();

        return NoContent();
    }
}
